<template>
    <div
        :class="[
            'el-input-number',
            size ? 'el-input-number--' + size : '',
            { 'is-disabled': disabled },
            { 'is-without-controls': !controls },
            { 'is-controls-right': controlsAtRight }
        ]"
    >
        <span 
            class="el-input-number__decrease"
            role="button"
            :class="{'is-disabled': minDisabled}"
            v-repeat-click="decrease"
        >
             <i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i>
        </span>
        <span
            class="el-input-number__increase"
            role="button"
            :class="{'is-disabled': maxDisabled}"
            v-repeat-click="increase"
        >
            <i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"></i>
        </span>
        <el-input
            ref="input"
            :size="size"
            :disabled="disabled"
            :value="displayValue"
            @input="handleInput"
            @change="handleInputChange"
        >
        </el-input>
    </div>
</template>

<script>
import ElInput from '../Input/index'
import RepeatClick from '../../directives/repeat-click';

export default {
    name: 'ElInputNumber',
    props: {
        value: {},
        step: {
            type: Number,
            default: 1
        },
        stepStrictly: {
            type: Boolean,
            default: false
        },
        max: {
            type: Number,
            default: Infinity
        },
        min: {
            type: Number,
            default: -Infinity
        },
        disabled: Boolean,
        precision: {
            type: Number,
            validator(val) {
                return val >= 0 && val === parseInt(val, 10);
            }
        },
        size: String,
        controls: {
            type: Boolean,
            default: true
        },
        controlsPosition: {
            type: String,
            default: ''
        },
    },
    directives: {
      repeatClick: RepeatClick
    },
    data() {
      return {
        currentValue: 0,
        userInput: null,
      };
    },
    computed: {
        minDisabled() {
            return this.value - this.step < this.min;
        },
        maxDisabled() {
            return this.value + this.step > this.max;
        },
        numPrecision() {
            const { value, step, getPrecision, precision } = this;
            const stepPrecision = getPrecision(step);
            if (precision !== undefined) {
                if (stepPrecision > precision) {
                    console.warn('[Element Warn][InputNumber]precision should not be less than the decimal places of step');
                }

                return precision;
            } else {
                return Math.max(getPrecision(value), stepPrecision);
            }
        },
        displayValue() {
            if (this.userInput !== null) {
                return this.userInput;
            }
            let currentValue = this.currentValue;
            if (typeof currentValue === 'number') {
                if (this.stepStrictly) {
                    const stepPrecision = this.getPrecision(this.step);
                    const precisionFactor = Math.pow(10, stepPrecision);
                    currentValue = Math.round(currentValue / this.step) * precisionFactor * this.step / precisionFactor;
                }
                if (this.precision !== undefined) {
                    currentValue = currentValue.toFixed(this.precision);
                }
            }
            return currentValue;
        },
        controlsAtRight() {
            return this.controls && this.controlsPosition === 'right';
        },
    },
    watch: {
      value: {
        immediate: true,
        handler(value) {
            let newVal = value === undefined ? value : Number(value);
            if (this.stepStrictly) {
                newVal = Math.round(newVal / this.step) * this.step
            }

            if (newVal >= this.max) newVal = this.max;
            if (newVal <= this.min) newVal = this.min;

            this.currentValue = newVal;
            this.userInput = null;
            this.$emit('input', newVal);
        }
      }
    },
    methods: {
        decrease() {
            if(this.minDisabled || this.disabled) return

            const value = this.value || 0;
            const newVal = this._decrease(value, this.step);
            this.setCurrentValue(newVal);
        },
        increase() {
            if(this.maxDisabled || this.disabled) return
            const value = this.value || 0;
            const newVal = this._increase(value, this.step);

            this.setCurrentValue(newVal);
        },
        _increase(val, step) {
            if (typeof val !== 'number' && val !== undefined) return this.currentValue;
            const precisionFactor = Math.pow(10, this.numPrecision);

            return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
        },
        _decrease(val, step) {
            if (typeof val !== 'number' && val !== undefined) return this.currentValue;
            const precisionFactor = Math.pow(10, this.numPrecision);
            return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor);
        },

        getPrecision(value) {
            if (value === undefined) return 0;
            const valueString = value.toString();
            const dotPosition = valueString.indexOf('.');
            let precision = 0;
            if (dotPosition !== -1) {
                precision = valueString.length - dotPosition - 1;
            }
            return precision;
        },
        toPrecision(num, precision) {
            if (precision === undefined) precision = this.numPrecision;
            return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision));
        },

        handleInput(value) {
            this.userInput = value;
        },
        handleInputChange(value) {
            let newVal = value === '' ? undefined : Number(value);
            if (!isNaN(newVal) || value === '') {
                this.setCurrentValue(newVal);
            }
            this.userInput = null;
        },
        setCurrentValue(newVal) {
            const oldVal = this.currentValue;

            if (newVal >= this.max) newVal = this.max;
            if (newVal <= this.min) newVal = this.min;
            if (oldVal === newVal) return;
            this.userInput = null;
            this.$emit('input', newVal);
            this.$emit('change', newVal, oldVal);
            this.currentValue = newVal;
        },
    },
    components: {
        ElInput
    }
}
</script>